package com.yf.internedstring;

import org.junit.Test;

public class StringInternTest {
    public static void main(String[] args) {
        String s3 = new String("1") + new String("1");
        s3.intern();
        String s4 = "11";
        System.out.println("s3 == s4 -> " + (s3 == s4)); // jdk6:false, jdk7:true, jdk8.321:true, >=jdk9:false
    }

    @Test
    public void test1() {
        //在堆中生成对象"1"，同时在字符串常量池中也创建了"1"
        String s1 = new String("1");
        //检查字符串常量中是否有"1"，这里显然已经有了，可以忽略
        s1.intern();
        //s2引用了字符串常量池中的"1"
        String s2 = "1";
        // s1是堆中的对象，而s2是字符串常量池中的对象
        System.out.println(s1 == s2); // false
    }

    @Test
    public void test2() {
        String s3 = new String("1") + new String("1");
        // s3指向 new String("11"),但字符串常量池中不存在"11"
        s3.intern();
        // JDK6：由于字符串常量池位于方法区，在字符串常量池中新生成了一个字符串对象"11"
        // JDK7及以上：由于字符串常量池移到了堆区，不需要考虑内存泄漏的问题，为了节省空间，没有必要在字符串常量池中创建副本，
        // 只创建了一个引用堆空间中的字符串对象
        String s4 = "11"; // s4指向字符串常量池中的"11"
        System.out.println("s3 == s4 -> " + (s3 == s4)); // jdk6:false, jdk7:true, jdk8.321:false, >=jdk9:false
    }

    @Test
    public void test3() {
        // 在字符串常量池中生成"ab"
        String s = "ab";
        // 相当于new String(byte[] buf), buf中保存了'a','b', 所以不会操作常量池
        String s1 = new String("a") + new String("b");
        // 此时，字符串常量池中已存在"ab",直接返回引用
        String s2 = s.intern();
        // 此时s2指向常量池中的"ab",s1指向堆空间中的"ab"对象,"ab"字面量从常量池获取
        System.out.println(s1 == "ab"); // false
        System.out.println(s2 == "ab"); // true
    }

    @Test
    public void test4() {
        //在堆空间中生成String实例，还会在字符串常量池中生成"ab"
        String s1 = new String("ab");
        // 只会在堆空间中生成String实例，不操作字符串常量池
//        String s1 = new String("a") + new String("b");
        s1.intern();
        String s2 = "ab";
        System.out.println(s1 == s2);
    }
}
